package net.sparrow.event;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import net.sparrow.Sparrow;
import net.sparrow.cmd.CommandHandler;
import net.sparrow.data.UtilFile;
import net.sparrow.module.Module;
import net.sparrow.util.Util;

public class SpModuleEntry {

	
	Module mod;
	HashMap<String, Field> dataFields = new HashMap<String, Field>();
	CommandHandler cmdHandler;
	
	public SpModuleEntry(Module m) {
		mod = m;
		cmdHandler = mod.loadCommands();
		try {
			wireDataFields();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public Set<String> getAllDataIds() {
		return dataFields.keySet();
	}
	
	public Module getModule() {
		return mod;
	}
	
	public boolean executeCommand(String[] args) {
		if(!cmdHandler.handleCommand(args)) {
			return mod.onCommand(args);
		}
			return true;
	}
	
	
	public void wireDataFields() throws Exception {
		List<Field> allFields = Arrays.asList(mod.getClass().getDeclaredFields());
		
		for (Field f : allFields) {
			DataField a = f.getAnnotation(DataField.class);
			if (a == null) { //not a data-wired field
				continue;
			}
			if(f.get(mod) == null) {
				Util.debug("Tried to wire up a field '" + f.getName()  + "' that doesn't have a default! Declare it!");
				continue;
			}
			
			Class fieldClass = f.getType();
			if (fieldClass.isAssignableFrom(List.class)) { // this is a list. lets make sure the list is of an ok type
				ParameterizedType stringListType = (ParameterizedType) f.getGenericType();
				Class<?> listClass = (Class<?>) stringListType.getActualTypeArguments()[0];
				if (!UtilFile.isClassJSONCompatible(listClass)) {
					Util.debug("Tried to wire up a field '" + f.getName() + "' that isn't JSON-compatible.");
					Util.debug("lists can only be of double/integer/string/boolean.");
					continue;
				}
			} else {
			if(!UtilFile.isClassJSONCompatible(fieldClass)) {
				Util.debug("Tried to wire up a field '" + f.getName() + "' that isn't JSON-compatible.");
				Util.debug("numbers (Double, Integer, etc), Strings, Boolean, and lists thereof are the only allowed types.");
				continue;
			}
			}
			
			dataFields.put(a.id(), f);
			//Sparrow.data().addToData(a.id(), newModule, f);

			Util.debug("Wired up data field '" + a.id() + "', connects to variable '" + f.getName() + "'.");
		}
	}
	
	public boolean contains(String id) {
		return dataFields.keySet().contains(id);
	}
	
	public Object getData(String id) {
		if(dataFields.get(id) == null) {
			return null;
		}
		try {
			return dataFields.get(id).get(mod);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	public void setData(String id, Object value) throws IllegalArgumentException {
		//value = Util.convertStringToObject(input)
		try {
			dataFields.get(id).set(mod, value);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}
	
	public void addData(String id, Object value) throws IllegalArgumentException {
		try {
			Object dataFieldObj = dataFields.get(id).get(mod);
			if(!(dataFieldObj instanceof List)) {
				Util.print("Tried to add to non-list object '" + id + "'!");
				throw new IllegalArgumentException();
			}
			
			try {
				List<Object> dataList = (List<Object>) dataFieldObj;
				dataList.add(value);
				Util.print("Added " + value + " to list.");
			} catch(Exception e) {
				Util.print("Tried to add an entry to a list of a different type. ID: " + id);
				throw new IllegalArgumentException();
			}
		} catch(IllegalAccessException e) {
			e.printStackTrace();
		}
	}
	
	public void remData(String id, Object value) {
		try {
			Object dataFieldObj = dataFields.get(id).get(mod);
			if(!(dataFieldObj instanceof List)) {
				Util.print("Tried to remove from non-list object '" + id + "'!");
				throw new IllegalArgumentException();
			}
			
			try {
				List<Object> dataList = (List<Object>) dataFieldObj;
				dataList.remove(value); //doesn't necessarily throw a ClassCastException
				Util.print("rem " + value + " to list.");
			} catch(Exception e) {
				Util.print("Tried to remove an entry to a list of a different type. ID: " + id);
				throw new IllegalArgumentException();
			}
		} catch(IllegalAccessException e) {
			e.printStackTrace();
		}
	}
	
}
